package eval.validation;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import eval.deploy.IDemandManager;
import eval.deploy.IDeployManager;
import eval.engine.IPlacement;
import eval.host.Host;
import eval.utilities.configuration.ExperimentConfiguration;
import eval.vm.IVMManager;
import eval.vm.VirtualMachine;

/**
 *Checks to perform:
 * VMSrequestedTobedeployed = PlacedVMS + RejectedVMS
 * 
 *
 */

public class SanityCheck {

	private ExperimentConfiguration configuration = ExperimentConfiguration.getSingleton();
	
	private IDeployManager deployManager;
	private IVMManager vmManager;
	private IDemandManager demandManager;
	


	public SanityCheck(IDeployManager deployManager, IVMManager vmManager ,IDemandManager demandManager) {
		this.deployManager = deployManager;
		this.vmManager = vmManager;
		this.demandManager = demandManager;
	}
	
	
	
	
	public Boolean runSaintyCheck(IPlacement curPlacement, IPlacement prevPlacement, int tick,FileWriter out) throws IOException{
		boolean isThereAnyError = false;
	 
		
		Map<Host, List<VirtualMachine>> CURvmMapping =  curPlacement.getvmMapping();
		List<VirtualMachine> VMSrequestedTobedeployed = curPlacement.getVMSrequestedTobedeployed();
		List<VirtualMachine> RejectedVMS = curPlacement.getRejectedVMS();
		List<VirtualMachine> PlacedVMS = curPlacement.getPlacedList();
		List<VirtualMachine> RemovedList = curPlacement.getRemovedList();
		//TODO
		//int[] HostLoadatPlacement = curPlacement.getHostLoadAtPlacement();
		
		List<VirtualMachine> PlacedAndRejected = new ArrayList<VirtualMachine>();
		PlacedAndRejected.addAll(PlacedVMS);
		PlacedAndRejected.addAll(RejectedVMS);
		if(!VMSrequestedTobedeployed.containsAll(PlacedAndRejected) ||  !(PlacedAndRejected.containsAll(VMSrequestedTobedeployed)) ){
			isThereAnyError = true;
			out.write("Some vm's from the deploy set are ignored.at tick: " + tick +"\n");
		}
		if(VMSrequestedTobedeployed.size() != (RejectedVMS.size() + PlacedVMS.size())){
			isThereAnyError = true;
			out.write("VMSrequestedTobedeployed != RejectedVMS + PlacedVMS at tick: "+tick+"\n");
		}
		if(curPlacement.getTotalCloudCPULoad() > curPlacement.getTotalCloudCpuDemand()){
			isThereAnyError = true;
			out.write("TotalCloudCPULoad > TotalCloudCpuDemand at tick:"+tick+"\n");
			//out.write(tick);
		}
		if(curPlacement.getTotalCloudCpuDemand() != curPlacement.getTotalSatisfiedCPUDemand() + curPlacement.getTotalUnSatisfiedCPUDemand()){
			isThereAnyError = true;
			out.write("TotalCloudCpuDemand != TotalSatisfiedCPUDemand + TotalUnSatisfiedCPUDemand at tick: "+tick+"\n");
			//out.write(tick);
		}
		
		if(configuration.totalNumberOfHosts != curPlacement.getNumberOfOpenHosts() + curPlacement.getNumberOfClosedHosts() + curPlacement.getNumberOfCandidatesForClosingHosts()){
			isThereAnyError = true;
			out.write("Error(#5):total number of hosts is wrong. at tick: " + tick + "\n");
		}

		if(curPlacement.getNumberOfTurnOffHosts() > 0 && curPlacement.getNumberOfTurnOnHosts() > 0){
			isThereAnyError = true;
			out.write("Error(#4): we try to open and close at the same placement. " + tick  + "\n" );
		}
		
		
		int NumberOfVMsInCloud = 0;
		for(Integer hostID: curPlacement.getCloudMap().keySet()){
			NumberOfVMsInCloud += curPlacement.getCloudMap().get(hostID).size();
		}
		
		if(curPlacement.getNumberOfVMsAtCloud() !=  NumberOfVMsInCloud){
			isThereAnyError = true;
			out.write("Error(#6):the total number of vm's at cloud are wrong. at tick: " + tick + "\n");
		}
		
		// iterates over host within the cloud
		int MemConsumptionInHost;
		int CpuDemandInHost;

		for (Host host : CURvmMapping.keySet()) {
			
			MemConsumptionInHost = 0;
			CpuDemandInHost = 0;
			//TODO
			/*
			if(HostLoadatPlacement[host.getHostId()] > host.getHostCapacity().getCpuCapacity()){
				isThereAnyError = true;
				out.write(tick);
			}
			if(host.isClose() && CURvmMapping.get(host).size() > 0){
				isThereAnyError = true;
				out.write("Error(#4):host " + host.getHostId() +" appear as closed but still containe vm's in it.found at tick: " + tick + "\n");
			}
			if(HostLoadatPlacement[host.getHostId()] > ((host.getHostCapacity().getCpuCapacity() * configuration.HostRedLine) / 100) ){
				isThereAnyError = true;
				out.write("Error(#):Host " + host.getHostId() + " crossed his red line. " + configuration.HostRedLine  + "% at tick: " + tick + "\n");
			}
			*/
			// iterates over vms within the host
			for (VirtualMachine vm : CURvmMapping.get(host)) {
				MemConsumptionInHost += demandManager.getRealDemand(vm).getMEMDemandAtTick(tick);
				//CpuDemandInHost += // demandManager.getRealDemand(vm).getCPUDemandAtTick(tick);
				
				if(RemovedList.contains(vm)){
					isThereAnyError = true;
					out.write("Error(#2): vm: " + vm.getIdentifier() + " is spouse to be removed but still appear in cloud.found at tick: " + tick + "\n");
				}
				if(RejectedVMS.contains(vm)){
					isThereAnyError = true;
					out.write("Error(#3): vm: " + vm.getIdentifier() + " is spouse to be rejected but appear in cloud.found at tick: " + tick + "\n");
				}
				
			}//end: iterates over vms within the host
			
			if(MemConsumptionInHost > host.getHostCapacity().getMemCapacity()){
				isThereAnyError = true;
				out.write("Error(#1): the total memory consumption of vm's in host number "
							+ host.getHostId() +" is bigger than memory capaity.tick number: " + tick + "\n");
			
			}
//			if(HostLoadatPlacement[host.getHostId()] > CpuDemandInHost){
//				isThereAnyError = true;
//				out.write("loud is bigger than demand.host: "+ host.getHostId() +" at tick: " +tick);
//			}
			
		}

	  
		
	return isThereAnyError;
	}
	
}